home *** CD-ROM | disk | FTP | other *** search
- _TEXT SEGMENT WORD PUBLIC 'CODE'
- _TEXT ENDS
-
- _DATA SEGMENT WORD PUBLIC 'DATA'
- _DATA ENDS
-
- CONST SEGMENT WORD PUBLIC 'CONST'
- CONST ENDS
-
- _BSS SEGMENT WORD PUBLIC 'BSS'
- ;GLOBAL UNINITIATED DATA GOES HERE
- _BSS ENDS
-
- DGROUP GROUP CONST,_BSS,_DATA
- ;
- ASSUME DS:DGROUP,SS:DGROUP
-
- ;EXTRN EXTERNAL SUBROUTINE CALLS GO HERE
- ;
- ; _pcplay FAR Procedure for C Written by John A. Ball April 22, 1996
- ;
- ;Plays back sound on the regular pc speaker
- ;
- ; void pcplay(char *buffer,unsigned number_read,unsigned frequency
- ; ,unsigned volume);
- ;
- ; pcplay(Buffer Pointer,Length,Frequency,Volume);
- ;
- ; Buffer Pointer is a Far pointer to the sound buffer (64k max)
- ; Length is the number of sound samples to play
- ; Frequency is the playback frequency in hertz
- ; Volume is currently not supported
- ;
- _TEXT SEGMENT
- ASSUME CS:_TEXT
- PUBLIC _pcplay
-
- _pcplay PROC FAR
-
- PARMA EQU [BP+6] ;Sound Buffer Pointer
- PARMB EQU [BP+10] ;Number of samples to play (Length)
- PARMC EQU [BP+12] ;Frequency to playback samples
- PARMD EQU [BP+14] ;Volume 0-7
- PARMS EQU 10 ;Number of bytes for local storage
-
- BUFFERPNT EQU [BP-4] ;LOCAL VARIABLES GO HERE
- SAMPLES EQU [BP-6]
- FREQUENCY EQU [BP-8]
- VOLUME EQU [BP-10]
-
- PUSH BP ;Save stack Frame
- MOV BP,SP
- SUB SP,PARMS ;MAKE SPACE FOR LOCAL VARIABLE (INT) ON STACK
- PUSH DS ;Save registers
- PUSH ES
- PUSH SI
- PUSH DI
- MOV CS:ERROR,0 ;Initialize error
- JMP OVERDATA
-
- DDELAY DW 0
- TCOUNT DW 0
- MCOUNT DW 0
- LCOUNT DW 0
- PERIOD DW 0
- DOUBLER DW 0
- ERROR DW 0
-
- TABLE DB 53,53,53,53,53,52,52,52,52,52,52,52,52,52,52,51
- DB 51,51,51,51,51,51,51,51,50,50,50,50,50,50,50,50
- DB 49,49,49,49,49,49,49,49,49,48,48,48,48,48,48,48
- DB 48,47,47,47,47,47,47,47,46,46,46,46,46,46,46,45
- DB 45,45,45,45,45,45,44,44,44,44,44,44,43,43,43,43
- DB 43,43,42,42,42,42,42,42,41,41,41,41,41,40,40,40
- DB 40,40,39,39,39,39,39,38,38,38,38,37,37,37,37,36
- DB 36,36,35,35,35,34,34,34,33,33,32,32,31,30,29,27
- DB 25,24,23,22,22,21,21,20,20,19,19,19,18,18,18,17
- DB 17,17,17,16,16,16,16,15,15,15,15,14,14,14,14,14
- DB 13,13,13,13,12,12,12,12,12,12,11,11,11,11,11,10
- DB 10,10,10,10,10,9,9,9,9,9,9,9,8,8,8,8
- DB 8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6
- DB 6,5,5,5,5,5,5,5,4,4,4,4,4,4,4,4
- DB 3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2
- DB 2,2,1,1,1,1,1,1,1,1,1,0,0,0,0,0
-
- OVERDATA:
- LES DI,PARMA ;GET SOUND BUFFER POINTER
- MOV BUFFERPNT,DI ;AND SAVE
- MOV DI,ES
- MOV BUFFERPNT+2,DI
-
- MOV BX,PARMB ;Get number of samples
- CMP BX,0 ;Check for zero samples
- JNE NORESET
- MOV CS:DDELAY,0 ;Reset delay if zero samples
- JMP FINISHED
- NORESET:
- MOV SAMPLES,BX ;AND SAVE
-
- MOV CS:DOUBLER,0
- MOV BX,PARMC ;Get frequency
- CMP BX,0 ;CHECK FOR DIVIDE BY ZERO
- JNE OK0
- MOV BX,11000 ;use default if zero
- OK0:
- CMP BX,0FFFFH ;Is frequency > 32,767 hz?
- JL OK21
- CMP BX,15000 ;Is frequency > 15,000 hz?
- JG OK1
- MOV CS:DOUBLER,2 ;Indicate frequency to be doubled
- SHL BX,1 ;Double frequency for play back
- OK1: CMP BX,22000 ;Is frequency 22,000 hz?
- JL OK2
- OK2: CMP BX,0FFFFH ;Is frequency 44,000 hz?
- JG OK3
- OK21: MOV CS:DOUBLER,4 ;Indicate frequency to be halved
- SHR BX,1 ;Halve frequency for playback
- OK3: MOV FREQUENCY,BX ;and save
- MOV DX,000FH ;CALCULATE PERIOD 1000000/FREQUENCY
- MOV AX,04240H
- DIV BX
- MOV CS:PERIOD,AX
- ;
- CPUSPEED:
- CMP CS:DDELAY,0 ;SKIP IF ALREADY DONE
- JE GETSPEED
- JMP BEGIN
-
- GETSPEED:
- IN AL,61H ;DISCONNECT SPEAKER FROM TIMER CHIP
- PUSH AX ;SAVE STATUS BITS
- AND AL,11111100B ;AND STOP TIMER
- OUT 61H,AL
- JMP $+2
- JMP $+2
- MOV AL,0B4H ;PROGRAM TIMER 2 FOR MODE 2
- OUT 43H,AL
- JMP $+2
- JMP $+2
- MOV AL,0 ;SET TIMER COUNT DOWN MAX COUNT
- OUT 42H,AL
- JMP $+2
- JMP $+2
- MOV AL,00H
- OUT 42H,AL
- JMP $+2
- JMP $+2
-
- POP AX ;START TIMER
- PUSH AX
- OR AL,00000001B
- OUT 61H,AL
- JMP $+2
- POP AX ;STOP TIMER
- AND AL,11111100B
- OUT 61H,AL
- JMP $+2
- IN AL,42H ;GET TIMER COUNT
- MOV BL,AL
- JMP $+2
- JMP $+2
- IN AL,42H
- MOV BH,AL ;BX = TIMER COUNT
- MOV AX,0 ;GET NUMBER OF COUNTS USED GETTING THE TIME
- SUB AX,BX
- MOV CS:TCOUNT,AX ;AND SAVE IT
-
- MOV SI,0
- MOV DI,0
- MOV CX,10 ;TIME for 10 loops
-
- MOV BH,0
- MOV AH,0
-
- CLI ;DISABLE INTERRUPTS DURING TEST
-
- IN AL,61H ;START TIMER
- PUSH AX
- JMP $+2
- OR AL,00000001B
- OUT 61H,AL
- ;
- ;USE ACTUAL LOOP FOR TIMING BUT DISABLE SPEAKER
- ;
- FETCH1:
- LDS BX,BUFFERPNT
- MOV AL,[BX+SI] ;Get sample
- MOV DI,AX
- MOV AL,CS:TABLE[DI] ;and convert to 6 bits
- OUT 0E0H,AL ;use E0h so it doesn't interfere with
- IN AL,0E0H
- OR AL,00000000B ;PRETEND TO START CLOCK
- OUT 0E0H,AL
-
- PUSH CX ;Save number of samples
- MOV CX,1 ;use minimum time
- DELAY1:
- JMP $+2
- LOOP DELAY1
-
- POP CX ;restore sample count
- INC SI
-
- IN AL,0E0H
- AND AL,1111111B ;PRETEND TO TURN OFF CLOCK
- OUT 0E0H,AL
-
- LOOP FETCH1
-
- POP AX ;STOP TIMER
- AND AL,11111100B
- OUT 61H,AL
-
- STI ;ENABLE INTERUPTS
-
- IN AL,42H ;GET TIMER COUNT
- MOV BL,AL
- JMP $+2
- JMP $+2
- IN AL,42H
- MOV BH,AL ;BX = TIMER COUNT
- MOV AX,0 ;65536 - TIMER COUNT = DURATION COUNT
- SUB AX,BX
- int 3
- nop
- SUB AX,CS:TCOUNT ;SUBTRACT COUNTS USED GETTING THE TIME
- MOV DX,0
- MOV BX,838
- MUL BX ;COUNT X 838 / 10000 = DURATION IN uSECs 100L
- MOV BX,10000
- DIV BX
- MOV BX,AX
- MOV AX,CS:PERIOD ;FIND OUT HOW MUCH TIME TO WASTE
- SUB AL,BL
- MOV AH,0
- MOV CS:MCOUNT,AX ;SAVE TIME
-
- MOV CX,100 ;TIME for 100 loops
- CLI ;DISABLE INTERRUPTS DURING TEST
-
- IN AL,61H ;START TIMER 2 AGAIN
- PUSH AX
- JMP $+2
- OR AL,00000001B
- OUT 61H,AL
-
- TIME1: JMP $+2
- LOOP TIME1
-
- POP AX
- AND AL,11111100B
- OUT 61H,AL
- IN AL,42H ;GET TIMER COUNT
- MOV BL,AL
- JMP $+2
- JMP $+2
- IN AL,42H
- MOV BH,AL ;BX = TIMER COUNT
- STI
- MOV DX,0
- MOV AX,0 ;NOTE THAT TIMER RESTARTS AT INIT COUNT
- SUB AX,BX ;IN MODE 2
- SUB AX,CS:TCOUNT ;SUBTRACT COUNTS USED GETTING THE TIME
- MOV DX,0
- MOV BX,838
- MUL BX ;COUNT X 838 / 1000 = DURATION IN uSECs 100L
- MOV BX,1000
- DIV BX
- MOV CS:LCOUNT,AX ;SAVE TEMPORARY VALUE
-
- MOV DX,0
- MOV AX,CS:MCOUNT ;GET TIME TO WASTE
- MOV BX,100
- MUL BX ;AND MULTIPLY BY 100
- MOV DX,0
- MOV BX,CS:LCOUNT ;AND DIVIDE BY LCOUNT
- CMP BX,0 ;CHECK FOR DIVIDE BY ZERO
- JNE OK
- JMP GETSPEED ;TRY AGAIN
- OK: DIV BX ;TO GET LOOPS REQUIRED
- AND AX,07FFH ;LIMIT TO REASONABLE VALUE
- MOV CS:DDELAY,AX ;SAVE LOOP COUNTER VALUE
- BEGIN:
- MOV AL,092H ;COUNTER 2, LSB ONLY, MODE 1, BINARY
- OUT 43H,AL
- MOV SI,0
- MOV DX,0
- MOV AH,0
- MOV CX,SAMPLES ;GET NUMBER OF SAMPLES TO PLAY
- cli
- CMP CS:DOUBLER,0 ;ADJUST PLAYBACK?
- JE FETCH
- JMP BEGIN1
-
- FETCH:
- LDS BX,BUFFERPNT
- MOV DL,[BX+SI] ;Get sample
- MOV DI,DX ;get index to table
- MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
-
- OUT 042H,AL
- IN AL,061H
- OR AL,00000011B ;START CLOCK
- OUT 061H,AL
- PUSH CX ;Save number of samples
- MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
- DELAY:
- JMP $+2
- LOOP DELAY
-
- pop cx ;RESTORE SAMPLE COUNT
- INC SI
-
- IN AL,061H
- AND AL,1111110B ;TURN OFF CLOCK
- OUT 061H,AL
-
- LOOP FETCH ;AND GO GET NEXT SAMPLE
-
- JMP FINISHED
-
- BEGIN1:
- CMP CS:DOUBLER,4 ;Halve playback?
- JE PLAYHALF
- FETCH3:
- LDS BX,BUFFERPNT
- MOV DL,[BX+SI] ;Get sample
- MOV DI,DX ;get index to table
- MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
-
- OUT 042H,AL
- IN AL,061H
- OR AL,00000011B ;START CLOCK
- OUT 061H,AL
- PUSH CX ;Save number of samples
- MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
- DELAY3:
- JMP $+2
- LOOP DELAY3
-
- NOP
- NOP
-
- IN AL,061H
- AND AL,1111110B ;TURN OFF CLOCK
- OUT 061H,AL
-
- LDS BX,BUFFERPNT
- MOV DL,[BX+SI] ;Get sample
- MOV DI,DX ;get index to table
- MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
-
- OUT 042H,AL ;Output sample twice at double frequency
-
- IN AL,061H
- OR AL,00000011B ;START CLOCK
- OUT 061H,AL
-
- MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
- DELAY4:
- JMP $+2
- LOOP DELAY4
-
- pop cx ;RESTORE SAMPLE COUNT
- INC SI
-
- IN AL,061H
- AND AL,1111110B ;TURN OFF CLOCK
- OUT 061H,AL
-
- LOOP FETCH3 ;AND GO GET NEXT SAMPLE
-
- JMP FINISHED
-
- PLAYHALF:
- SHR CX,1 ;Halve the number of samples
- FETCH4:
- LDS BX,BUFFERPNT
- MOV DL,[BX+SI] ;Get sample
- MOV DI,DX ;get index to table
- MOV AL,CS:TABLE[DI] ;and convert sample to timer pulse durations
-
- OUT 042H,AL
- IN AL,061H
- OR AL,00000011B ;START CLOCK
- OUT 061H,AL
- PUSH CX ;Save number of samples
- MOV CX,CS:DDELAY ;GET DELAY LOOP COUNTER
- DELAY5:
- JMP $+2
- LOOP DELAY5
-
- pop cx ;RESTORE SAMPLE COUNT
- INC SI ;Increment twice to skip sample
- INC SI
-
- IN AL,061H
- AND AL,1111110B ;TURN OFF CLOCK
- OUT 061H,AL
-
- LOOP FETCH4 ;AND GO GET NEXT SAMPLE
-
- FINISHED:
-
- sti
- MOV AH,01H ;See if key pressed
- INT 16H
- JZ ALL_DONE
- KEY_PRESS:
- MOV AX,0
- INT 16H ;Get the pressed key
- CMP AL,1BH ;See if ESC key hit?
- JNE ALL_DONE
- MOV CS:ERROR,-1 ;Halt output if ESC key pressed
- ALL_DONE:
- MOV AX,CS:ERROR ;Get error
- POP DI
- POP SI
- POP ES ;Restore registers
- POP DS
- ADD SP,PARMS ;RESTORE SP
- POP BP ;RESTORE BP
- RET ;RETURN FAR
-
- _pcplay ENDP
- _TEXT ENDS
- END
-
-